// On reprend les histoires de translation/rotation de fasson plus claire
// esperons...

// TODO : calculer la position du cercle de cardioide puis trouver le bon plan DONE
// TODO : on peut s'occuper de la position de la sphere apres..; DONE
// On voit si on peut faire bouger la sphere plus vers le lieu de corisement

import ij.ImagePlus;
import ij.plugin.PlugIn;
import ij.process.ColorProcessor;
import ij.process.ImageProcessor;


import java.awt.Color;


import java.awt.Font;

import java.awt.Polygon;
import java.io.PrintStream;

import java.util.Locale;
import java.util.Random;



import pluginUtils.Circle;
import pluginUtils.CircleArrangement;
import pluginUtils.Complexe;
import pluginUtils.DoyleCircle;
import pluginUtils.MobiusTransform;
import pluginUtils.Plane;
import pluginUtils.Pos3D;
import pluginUtils.Rotation3D;
import pluginUtils.Sphere;

import pluginUtils.PackCircle;


public class BeretCalculSphereQUiBouge extends ImagePlus implements PlugIn{
        {Locale.setDefault(Locale.US); }
       
        private ImageProcessor primeIP;
        private int X=2000,Y=2000; // image size
       
        private PrintStream output,blenderOutput;
        private double xShift=0;
        private double zShift=0;
       
       

        private Random generator=new Random(); // One always needs some randomness
       
        // For the scaling of the image
        private double x0=0,x1=1,y0=0,y1=1;
        private double acoef,ccoef,vcoef,wcoef;
       
        private String[] palette={"Yellow","Red","Blue","Green","White","NavyBlue","Turquoise","Orange"};
        private Color[] palette1={Color.YELLOW,Color.RED,Color.BLUE,Color.GREEN,Color.MAGENTA,Color.ORANGE};
        public void setCoefs(){
                acoef=X/(x1-x0);
                vcoef=Y/(y1-y0);
                ccoef=-acoef*x0;
                wcoef=-vcoef*y0;
        }
         
       
       
        public void setBounds(double xl,double xu,double yl,double yu){
                x0=xl;
                x1=xu;
                y0=yl;
                y1=yu;
        }
       
       
        private double sch=1.097; 
        private String T="T0"; 
        private String F="F0"; 
        
  
       
        private void drawCircleCardioid(Circle c,double rot,Complexe cc){
        	/*CI-dessous, ce qui est fait dans le bon dessin des tores(cardioiddrawinggabrielaworkversion)
        	 * String s="torus{"+c.getRadius()+",InternRad texture{T0} finish{F0}\n";
		s+=" rotate "+rot+"*x ";  
		
		s+="translate <1,0,0>\n"; 
		
		s+=" rotate "+c.getCenter().getArgument()*180/Math.PI +"*y\n}";  
		
		System.out.println(s); 
		output.println(s);
		blenderOutput.println(c.getRadius()+" "+c.getCenter().getReal()+" "+c.getCenter().getIm()+" "+rot+" "+(i%8)*32+" "+(i%4)*64+" "+(i%2)*128); 
	
        	
        	 */
        	
        	
        		String centre=("#declare centerCardio=vtransform(<0,0,0>,transform{\n");
        		centre+=" rotate "+rot+"*180/pi*x ";  
          		centre+="translate <"+(-sch)+",0,0>\n"; 
          		centre+=" rotate "+(c.getCenter().getArgument())*180/Math.PI +"*y\n});";  
                output.println(centre);
                //output.println("sphere{centerCardio,1.5*InternRad texture{pigment{color Red}}}\n");
                String s="torus{"+c.getRadius()+",InternRad texture{"+T+"} finish{"+F+"}\n";
                s+=" rotate "+rot+"*180/pi*x ";  
        		s+="translate <"+(-sch)+",0,0>\n"; 
        		s+=" rotate "+(c.getCenter().getArgument()*180/Math.PI) +"*y\n}";  
                System.out.println(s);
                output.println(s);
                // type 0
                blenderOutput.println("0 "+c.getRadius()+" "+(-sch)+" "+rot+" "+cc.getArgument());
       
        }
       
        private void drawCircle(Circle c,double rot,Complexe cc){
        	
        	
                String s="torus{"+c.getRadius()+",InternRad texture{"+T+"} finish{"+F+"}\n";
                s+=" rotate "+rot+"*180/pi*k*x ";  
                s+="translate <"+cc.getReal()+",0,"+cc.getIm()+">}\n";
                System.out.println(s);
                output.println(s);
                //blenderOutput.println(c.getRadius()+" "+c.getCenter().getReal()+" "+c.getCenter().getIm()+" "+rot);
       
        }
       
        private void drawPoint(Complexe c){
        	primeIP.setColor(Color.BLUE);
                double xCenter=c.getReal();
                double yCenter=c.getIm();
                int intX=(int)(xCenter*acoef+ccoef);
                int intY=(int)(yCenter*vcoef+wcoef);
                primeIP.fillOval(intX-10,intY-10,20,20);
        }
       
       
       
       
       
        private void drawLine(double xd,double yd,double xf,double yf){
                int XD=(int)(xd*acoef+ccoef);
                int YD=(int)(yd*vcoef+wcoef);
                int XF=(int)(xf*acoef+ccoef);
                int YF=(int)(yf*vcoef+wcoef);
                primeIP.drawLine(XD,YD,XF,YF);
        }
       
       
    	public String povrayCylinder(Pos3D u, Pos3D v) {
			String s="cylinder{"+u.toPovrayVector()+","+v.toPovrayVector()+",diametre texture{T0}}"; 
			return s;
		}
       
       
       
       
        public void run(String arg) {
                primeIP = new ColorProcessor(X,Y);
               
               
                double limit=5;
       
                setBounds(-limit,limit,-limit,limit);
                setCoefs();
               
               
               
                Polygon background=new Polygon(new int[]{0,0,X,Y},new int[]{0,X,Y,0},4);
                primeIP.setColor(Color.BLACK);
               
                primeIP.fillPolygon(background);
                primeIP.setLineWidth(5);
                primeIP.setColor(Color.WHITE);
                drawLine(0,-limit,0,limit); 
                drawLine(-limit,0,limit,0); 
                
                primeIP.setColor(Color.BLUE);
                drawLine(1,-limit,1,limit); 
                primeIP.setColor(Color.GREEN);
                drawLine(-limit,1,limit,1); 
                
               
                primeIP.setFont(new Font(null,Font.ITALIC,60));
                primeIP.setColor(Color.WHITE);
                primeIP.setLineWidth(5);

                try{
                        output=new PrintStream("/tmp/Beret.txt");
                        // dumb : rien pour le moment
                        blenderOutput=new PrintStream("/tmp/BeretSansTrou.txt");
                        }
                catch(Exception e){System.out.println("erreur "+e); System.exit(0); }
               
                setProcessor("Verifying", primeIP);
                
                
                int subdiv=150;
               
                Complexe origine=Complexe.createComplexeFromPolar(1*sch,0);
                
                
                //Sphere tete=new Sphere(new Pos3D(1.1922,-0.9675,-0.813),1.45);
               
                Sphere tete=new Sphere(new Pos3D(1.0,1.05,-0.362),1.45 );
                //Sphere tete=new Sphere(new Pos3D(1.2,1.075,-0.5),1.45 );
                //output.println("sphere{<"+tete.getCenter().getX()+","+tete.getCenter().getY()+","+tete.getCenter().getZ()+">,1.45 texture{pigment{color rgbt <0,1,0,0.0>}}}\n");
    	  		
                
                primeIP.setColor(Color.GREEN);
                Circle original=new Circle(Complexe.ZERO,sch);
          	    //drawCircle(original,0,Complexe.ZERO); 
                int nbcuts=0; 
                
                output.println("#declare side0=array[149]\n"); 
                output.println("#declare side1=array[149]\n"); 
                for(int i=0;i<subdiv;i++){
                	  Complexe z=Complexe.createComplexeFromPolar(1*sch,i*2*Math.PI/subdiv);
                	  double coef=Math.abs(Math.cos(0.525*i/(0.0+subdiv))); // 0.5
                	  //double rot=i*2*Math.PI/subdiv;
                	  double rot=2*coef*Math.PI; 
                     // Calculer la transformation (rotation X, decalage, rotation
                	  double dist=Complexe.distance(z, origine); 
                	  System.out.println(i+" "+dist); 
                	  //Pos3D OrigineDecalee=new Pos3D(sch*Math.cos(rot),0,sch*Math.sin(rot)); // equivalent a z
          	  		  Rotation3D laRotationX=new Rotation3D('x', rot);
          	  		  Rotation3D laRotationXInverse=new Rotation3D('x',-rot);
          	  		  Rotation3D laRotationY=new Rotation3D('y',z.getArgument() );
        	  		  Rotation3D laRotationYInverse=new Rotation3D('y',-z.getArgument());
                	  
                	  
                	 Pos3D centreCardio=new Pos3D(0,0,0); 
                	 // premiere rotation
                	 centreCardio=laRotationX.rotate(centreCardio);
                	 // translation
                	 centreCardio.setX(centreCardio.getX()-sch);
                	 // deuxieme rotation
                	 centreCardio=laRotationY.rotate(centreCardio); 
                	
                	
                	  
                	  if(Complexe.distance(z, origine)>0.00001){
                		  T="T1"; 
              	  		  F="F1"; 
              	  		// Le cercle de la cardioide : centre z, rayon distance(z,origine)
              	  		  Circle fromCardioid=new Circle(z,Complexe.distance(z,origine));
              	  		
                		 if(i<222){
                		
                			 
                			 String centre=("#declare centerCardio=vtransform(<0,0,0>,transform{\n");
                     		centre+=" rotate "+rot+"*180/pi*x ";  
                       		centre+="translate <"+(-sch)+",0,0>\n"; 
                       		centre+=" rotate "+(z.getArgument())*180/Math.PI +"*y\n});";  
                             output.println(centre);
                			 //drawCircleCardioid(fromCardioid,rot,z); 
                			 //output.println("sphere{centerCardio,0.02 texture{pigment{color Yellow}}}\n"); 
                			
                	  		/* Equation du plan  */
                			// Vecteur normal : direction verticale
                			 Pos3D fleche=new Pos3D(0,1,0); 
                			 System.out.println("A "+fleche); 
                			 fleche=laRotationX.rotate(fleche);
                			 System.out.println("B "+fleche); 
                			// fleche.setX(fleche.getX()+sch); 
                			 System.out.println("C "+fleche); 
                			 fleche=laRotationY.rotate(fleche);
                			 System.out.println("D "+fleche); 
                	  		// Calculer les intersections de la grosse sphere et de chacun de ces plans
                	  		Plane planLocal=new Plane(new Pos3D(fleche.getX(),fleche.getY(),fleche.getZ()),centreCardio);
                	  		// dessiner le plan local
                	  		//output.println("plane{<"+planLocal.getA()+","+planLocal.getB()+","+planLocal.getC()+">,"+(-planLocal.getD())+" texture{pigment{color Yellow}}}");
               		  
                	  		// centre du cercle d'intersection
                	  		Pos3D H=planLocal.projectionOrthogonale(tete.getCenter());
                	  	    //output.println("sphere{<"+H.getX()+","+H.getY()+","+H.getZ()+">,0.05 texture{pigment{color Red}}}\n");
                	  		System.out.println("distance H plan "+planLocal.distancePoint(H)); 
                	  		// H est le centre du cercle d'intersection de la sphere et du plan
                	  		 
                	  		//output.println(povrayCylinder(tete.getCenter(),H)); 
                	  		
                	  		
                	  		
                	  	
                	  		 
                       	 String centreBis=("#declare centerSphere=<"+H.getX()+","+H.getY()+","+H.getZ()+">;");
                            
                           output.println(centreBis);
                	  		
                	  		
                	  		
                	  		//System.out.println("H "+H); 
                	  	
                	  		
                	  		// Rayon du cercle d'intersection
                	  		double disCenter=planLocal.distancePoint(tete.getCenter()); 
                	  		double rayon=tete.getRadius()*tete.getRadius()-disCenter*disCenter; 
                	  		T="T0"; 
                	  		F="F0"; 
                	  		// la sphere de la tete intersecte le plan contenant le cercle de la cardioide
                	  		if(rayon>=0){
                	  			
                	  			//System.out.println("ok ca trace"); 
                	  			rayon=Math.sqrt(rayon); 
                	  			// le cercle sur la sphere : centre Hap, rayon rayon
                
                	  			// La suite n'a de sens que si les cercles s'intersectent
                	  			//System.out.println("Rayon "+rayon); 
                	  			//System.out.println("dist "+dist); 
                	  			//System.out.println("distance des centres "+Pos3D.distance(H,centreCardio));
                	  			double dic=Pos3D.distance(H,centreCardio); 
                	  			if((rayon+dist>dic)
                	  				&&(dist+dic>rayon)
                	  				&&(rayon+dic>dist)
                	  					)	
                	  					{
                	  				
                	  				System.out.println(i+"  intersection                      *************"); 
                	  			// Droite des centres
                	  			//output.println("cylinder{centerCardio,centerSphere,InternRad texture{pigment{color Blue}}}");
                	  			//milieu de la lentille
                	  			double vx=(dic*dic+(-rayon*rayon+dist*dist))/(2*dic);
                	  			//System.out.println("R*R-x*x "+(dist*dist-vx*vx)); 
                	  			
                	  			output.println("#declare disCenter=VDist(centerCardio,centerSphere);\n"); 
                	  			output.println("#declare proportion=(disCenter*disCenter+"+(-rayon*rayon+dist*dist)+")/(2*disCenter*disCenter);\n");
                	  			output.println("#declare midLens=(1-proportion)*centerCardio+proportion*centerSphere; \n");
                	  			
                	  			//output.println("sphere{midLens,1.5*InternRad texture{pigment{color Red}}}\n");
                	  			// Calculer les deux points d'intersection
                	  			// Produit vectoriel avec le vecteur normal au plan
                	  			//output.println("#declare cp=vnormalize(vcross(centerCardio-centerSphere,<0,"+(-Math.cos(rot))+","+(-Math.sin(rot))+">));\n");
                	  			output.println("#declare cp=vnormalize(vcross(centerCardio-centerSphere,<"+fleche.getX()+","+fleche.getY()+","+fleche.getZ()+">));\n");
                	  			// Longueur du deplacement
                	  			double deplacement=Math.sqrt(dist*dist-vx*vx);
                	  			//System.out.println("valeur de y "+deplacement); 
                	  			
                	  			output.println("#declare deplacement="+deplacement+";\n"); 
                	  			//output.println("cylinder{midLens+deplacement*cp,midLens-deplacement*cp,InternRad texture{pigment{color Green}}}\n");
                	  			output.println("#declare inter0=midLens+deplacement*cp; \n");
                	  			output.println("#declare inter1=midLens-deplacement*cp; \n");
                	  			output.println("#declare side0["+nbcuts+"]=inter0;\n"); 
                	  			output.println("#declare side1["+nbcuts+"]=inter1;\n"); 
                	  			output.println("sphere{inter0,2.414*InternRad texture{pigment{color Black}}}\n");
                	  			output.println("sphere{inter1,2.414*InternRad texture{pigment{color Black}}}\n");
                	  			
                	  			// DEBUG : dessiner les spheres de l'intersection dans blender
                	  			String p0="str(inter0.x,0,-1),\" \", str(inter0.y,0,-1),\" \", str(inter0.z,0,-1)"; 
           	  			        String p1="str(inter1.x,0,-1),\" \", str(inter1.y,0,-1),\" \", str(inter1.z,0,-1)"; 
                	  			 output.println("#declare instruction=\"3\" "); // typeObjet des spheres de debug
            	  			     output.println("#declare instruction=concat(instruction,\" \","+p0+",\"\\n\")");
            	  			     output.println("#debug instruction");
            	  			     output.println("#declare instruction=\"3\" "); // typeObjet des spheres de debug
            	  			     output.println("#declare instruction=concat(instruction,\" \","+p1+",\"\\n\")");
          	  			     	 output.println("#debug instruction");
                	  			
                	  			// Suite : on ramene les intersections a plat au centre, et on decoupe un pacman de tore
                	  			// qu'on remet en place apres. 
                	  			
                	  			/* Modele de la transfo
                	  			centre+=" rotate "+rot+"*180/pi*x ";  
                          		centre+="translate <"+(-sch)+",0,0>\n"; 
                          		centre+=" rotate "+(z.getArgument())*180/Math.PI +"*y\n});";  
                	  			*/
                	  			
                	  			
                	  			
                	  			String i0Ocentre="vtransform(inter0,transform{\n";
                	  		    i0Ocentre+=" rotate "+(-z.getArgument())*180/Math.PI +"*y\n";  
                	  		    i0Ocentre+="translate <"+(sch)+",0,0>\n"; 
                	  	        i0Ocentre+=" rotate "+(-rot)+"*180/pi*x});\n";  
                	  	       
                	  	            
                	  			output.println("#declare inter0APlat="+i0Ocentre); 
                	  			
                	  		    String i1Ocentre="vtransform(inter1,transform{\n";
                	  		    i1Ocentre+=" rotate "+(-z.getArgument())*180/Math.PI +"*y\n";  
              	  		        i1Ocentre+="translate <"+(sch)+",0,0>\n"; 
              	  	            i1Ocentre+=" rotate "+(-rot)+"*180/pi*x});\n";  
           	  	            
           	  			        output.println("#declare inter1APlat="+i1Ocentre); 
           	  			         
           	  			         
           	  			        // Couper une tranche de tore
           	  			        // Un prism
           	  			        output.println("#declare camembert=prism{linear_spline -1,1,4\n"); 
           	  			        output.println("2*<inter0APlat.x,inter0APlat.z>,<0,0>,2*<inter1APlat.x,inter1APlat.z>,2*<inter0APlat.x,inter0APlat.z>}");
           	  			      
           	  			        String pacy="#declare pacman=object{difference{torus{"+dist+",InternRad texture{pigment{color Green}}} camembert\n";
           	  			        pacy+=" rotate "+rot+"*180/pi*x ";  
                          		pacy+="translate <"+(-sch)+",0,0>\n"; 
                          		pacy+=" rotate "+(z.getArgument())*180/Math.PI +"*y\n}};";  
                          		
           	  			        
           	  			        output.println(pacy); 
           	  			        output.println("object{pacman}");
           	  			        
           	  			        // Preparer la chaine de caracteres que devra produire povray pour definir un pacman
           	  			        output.println("#declare dist="+dist+";"); 
           	  			        String i0ap="str(inter0APlat.x,0,-1),\" \", str(inter0APlat.y,0,-1),\" \", str(inter0APlat.z,0,-1)"; 
           	  			        String i1ap="str(inter1APlat.x,0,-1),\" \", str(inter1APlat.y,0,-1),\" \", str(inter1APlat.z,0,-1)"; 
           	  			        output.println("#declare instruction=\"1\" "); // typeObjet des pacman
           	  			        output.println("#declare instruction=concat(instruction,\" \",str(dist,0,-1),\" \",str("+(-sch)+",0,-1),\" \",str("+rot+",0,-1),\" \",str("+z.getArgument()+",0,-1),\" \","+i0ap+",\" \","+i1ap+",\"\n\")");
           	  			        output.println("#debug instruction");
           	  			        
           	  			        nbcuts++; 
           	  			        }
                	  			
                	  			
                	  			else {
                	  				System.out.println(i+" pas d'intersection");
                	  				// Sinon, on dessine le tore complet
                	  				//drawCircleCardioid(new Circle(Hap,rayon),2*i*Math.PI/subdiv,z);
                	  			drawCircleCardioid(fromCardioid,rot,z); 
                	  			}
                	  		}// rayon>0
                		  
                	  		else drawCircleCardioid(fromCardioid,rot,z); 
                	  		}
                	  }
                        
                }
               // Les tores sont traces, on va relier les spheres par des cylindres
                
                output.println("#declare index=0; \n"); 
                output.println("#while(index<"+nbcuts+")\n"); 
                // trouver les deux distances les plus petites
                output.println("#declare dist1=VDist(side0[index],side0[mod((index+1),"+nbcuts+")]);\n"); 
                output.println("#declare dist2=VDist(side0[index],side1[index]);\n"); 
                output.println("#declare dist3=VDist(side0[index],side1[mod((index+1),"+nbcuts+")]);\n");
                output.println("#if((dist1<dist2)&(dist1<dist3))\n"); // cas normal
                output.println("#declare begin0=side0[index];\n"); 
                output.println("#declare end0=side0[mod((index+1),"+nbcuts+")];\n");
                output.println("#declare begin1=side1[index];\n"); 
                output.println("#declare end1=side1[mod((index+1),"+nbcuts+")];\n");
                output.println("cylinder{side0[index],side0[mod((index+1),"+nbcuts+")],InternRad texture{pigment{color Orange}}}\n"); 
                output.println("cylinder{side1[index],side1[mod((index+1),"+nbcuts+")],InternRad texture{pigment{color Blue}}}\n");
                output.println("#end\n"); 
                
                output.println("#if((dist2<dist1)&(dist2<dist3))\n"); // cas deux
                output.println("#declare begin0=side0[index];\n"); 
                output.println("#declare begin1=side0[mod((index+1),"+nbcuts+")];\n");
                output.println("#declare end0=side1[index];\n"); 
                output.println("#declare end1=side1[mod((index+1),"+nbcuts+")];\n");
                output.println("cylinder{side0[index],side1[index],InternRad texture{pigment{color Orange}}}\n"); 
                output.println("cylinder{side0[mod((index+1),"+nbcuts+")],side1[mod((index+1),"+nbcuts+")],InternRad texture{pigment{color Blue}}}\n");
                output.println("#end\n"); 
                
                output.println("#if((dist3<dist2)&(dist3<dist1))\n"); // cas trois
                output.println("#declare begin0=side0[index];\n"); 
                output.println("#declare end0=side1[mod((index+1),"+nbcuts+")];\n");
                output.println("#declare begin1=side1[index];\n"); 
                output.println("#declare end1=side0[mod((index+1),"+nbcuts+")];\n");
                output.println("cylinder{side0[index],side1[mod((index+1),"+nbcuts+")],InternRad texture{pigment{color Orange}}}\n"); 
                output.println("cylinder{side1[index],side0[mod((index+1),"+nbcuts+")],InternRad texture{pigment{color Blue}}}\n");
                output.println("#end\n"); 
                
                // TODO : fabriquer la sortie pour blender
                 output.println("#declare instruction=\"2\" "); // typeObjet des cylindres
 			        output.println("#declare instruction=concat(instruction,\" \",str(begin0.x,0,-1),\" \",str(begin0.y,0,-1),\" \",str(begin0.z,0,-1),\" \",str(end0.x,0,-1),\" \",str(end0.y,0,-1),\" \",str(end0.z,0,-1),\"\n\")");
 			        output.println("#debug instruction");
 			       output.println("#declare instruction=\"2\" "); // typeObjet des cylindres
			        output.println("#declare instruction=concat(instruction,\" \",str(begin1.x,0,-1),\" \",str(begin1.y,0,-1),\" \",str(begin1.z,0,-1),\" \",str(end1.x,0,-1),\" \",str(end1.y,0,-1),\" \",str(end1.z,0,-1),\"\n\")");
			        output.println("#debug instruction");
                output.println("#declare index=index+1;\n"); 
                output.println("#end\n"); 
                
                //show();
                output.close();
                System.out.println("done "+nbcuts); 
                
        }



	}
 